/*
 * Res.java
 *
 * Created on 2005/01/27 +0900
 *
 * $Revision: 38 $
 * $Date: 2008-10-11 08:48:45 +0000 (Sat, 11 Oct 2008) $
 */

package org.gikolet.bbs.data;

import java.util.Vector;

import org.gikolet.framework.util.StringUtilities;

/**
 * @author Tetsutaro (latest modification by $Author: tetsutaro.ss $)
 * @version $Revision: 38 $ $Date: 2008-10-11 08:48:45 +0000 (Sat, 11 Oct 2008) $
 */
public class Res {
    private ThreadHeader threadHeader;
    private int no;
    private String name;
    private String mailAddress;
    private String date;
    private String id;
    private String body;

    private Vector blocks;

    public Res(ThreadHeader threadHeader, int no, String name, String mailAddress,
	    String date, String id, String body) {

	this.threadHeader = threadHeader;
	this.no = no;
	this.name = name;
	this.mailAddress = mailAddress;
	this.date = date;
	this.id = id;
	this.body = body;

	kaiseki(body);
    }

    private void kaiseki(String str) {
	this.blocks = new Vector();

	int curTextOffset = 0;
	for (int i = 0; i < str.length(); i++) {
	    URILink uriLink = getURILink(str, i);
	    if (uriLink != null) {
		if (curTextOffset < i) {
		    this.blocks
			    .addElement(new TextBlock(str.substring(curTextOffset, i)));
		}
		this.blocks.addElement(uriLink);

		curTextOffset = i + uriLink.getText().length();
		i = curTextOffset - 1;
	    } else {
		InnerLink link = getInnerLink(str, i, str.length());
		if (link != null) {
		    if (curTextOffset < i) {
			this.blocks.addElement(new TextBlock(str.substring(curTextOffset,
				i)));
		    }

		    blocks.addElement(link);

		    curTextOffset = i + link.getText().length();
		    i = curTextOffset - 1;
		}
	    }
	}
	if (curTextOffset < str.length()) {
	    this.blocks.addElement(new TextBlock(str.substring(curTextOffset, str
		    .length())));
	}
    }

    private InnerLink getInnerLink(String str, int s, int t) {
	int iw = s;
	char c = str.charAt(iw);
	if (c == '>' || c == '＞') {
	    iw++;
	    if (t <= iw) {
		return null;
	    }
	    c = str.charAt(iw);
	    if (c == '>' || c == '＞') {
		iw++;
	    }
	} else {
	    return null;
	}

	if (t <= iw) {
	    return null;
	}
	c = str.charAt(iw);
	if (!isDigit(c)) {
	    return null;
	}

	int j = iw;

	while (true) {
	    if (t <= j) {
		break;
	    }
	    c = str.charAt(j);
	    if (isDigit(c)) {
		j++;
	    } else {
		break;
	    }
	}
	try {
	    int so = parseInt(str.substring(iw, j));
	    int to = so;

	    if (t > j) {
		c = str.charAt(j);
		if (c == '-' || c == '−') {
		    if (t > j + 1) {
			int jw = ++j;
			while (true) {
			    if (t > j && isDigit(str.charAt(j))) {
				j++;
			    } else {
				if (jw < j) {
				    to = parseInt(str.substring(jw, j));
				}
				break;
			    }
			}
		    }
		}
	    }

	    return new InnerLink(str.substring(s, j), (so < to) ? so : to, (so < to) ? to
		    : so);
	} catch (NumberFormatException e) {
	    return null;
	}
    }

    private boolean isDigit(char c) {
	return ((0x30 <= c && c <= 0x39) || (0xFF10 <= c && c <= 0xFF19));
    }

    private int parseInt(String str) throws NumberFormatException {
	StringBuffer sb = new StringBuffer(str);
	for (int i = 0; i < str.length(); i++) {
	    char c = str.charAt(i);
	    if (0xFF10 <= c && c <= 0xFF19) {
		sb.setCharAt(i, (char) (c - (char) 0xFEE0));
	    }
	}
	return Integer.parseInt(sb.toString());
    }

    private URILink getURILink(String text, int index) {
	// ttp://
	if (index < 0 || text.length() < index + 6) {
	    return null;
	}

	int nowIndex = index;
	char c = text.charAt(nowIndex);
	if (c == 'h' || c == 'ｈ') {
	    if (text.length() < ++nowIndex + 6) {
		return null;
	    }
	}

	// 冗長的な方が圧縮率が高いんではないだろうかぁぁぁ！！
	// …深夜3時に何をやってるんだろうかorz
	c = text.charAt(nowIndex++);
	if (c != 't' && c != 'ｔ') {
	    return null;
	}
	c = text.charAt(nowIndex++);
	if (c != 't' && c != 'ｔ') {
	    return null;
	}
	c = text.charAt(nowIndex++);
	if (c != 'p' && c != 'ｐ') {
	    return null;
	}
	c = text.charAt(nowIndex++);
	if (c == 's' || c == 'ｓ') {
	    if (text.length() < nowIndex + 3) {
		return null;
	    }
	    c = text.charAt(nowIndex++);
	}
	if (c != ':' && c != '：') {
	    return null;
	}
	c = text.charAt(nowIndex++);
	if (c != '/' && c != '／') {
	    return null;
	}
	c = text.charAt(nowIndex);
	if (c != '/' && c != '／') {
	    return null;
	}

	// 英数字「;」「/」「?」「:」「@」「&」「=」「+」「$」「,」
	// 「-」「_」「.」「!」「~」「*」「'」「(」「)」「%」「#」
	int endIndex;
	while (true) {
	    if (text.length() <= nowIndex) {
		endIndex = nowIndex;
		break;
	    }
	    c = text.charAt(nowIndex);
	    if (Character.isDigit(c) || (0x61 <= c && c <= 0x7A)
		    || (0x41 <= c && c <= 0x5A) || c == ';' || c == '/' || c == '?'
		    || c == ':' || c == '@' || c == '&' || c == '=' || c == '+'
		    || c == '$' || c == ',' || c == '-' || c == '_' || c == '.'
		    || c == '!' || c == '~' || c == '*' || c == '\'' || c == '('
		    || c == ')' || c == '%' || c == '#') {
		nowIndex++;
	    } else {
		endIndex = nowIndex;
		break;
	    }
	}
	String str = text.substring(index, endIndex);
	String uri = StringUtilities.toAlphaHalf(str);
	if (str.startsWith("ttp")) {
	    uri = 'h' + str;
	}
	return new URILink(str, uri);
    }

    public ThreadHeader getThreadHeader() {
	return this.threadHeader;
    }

    public int getNo() {
	return this.no;
    }

    public String getName() {
	return this.name;
    }

    public String getMailAddress() {
	return this.mailAddress;
    }

    public String getDate() {
	return this.date;
    }

    public String getID() {
	return this.id;
    }

    public String getBody() {
	return this.body;
    }

    public int getBlockSize() {
	return this.blocks.size();
    }

    public TextBlock getBlock(int index) {
	return (TextBlock) this.blocks.elementAt(index);
    }

    public String toString() {
	return "no:" + this.no + " name:" + this.name + " mailAddress:"
		+ this.mailAddress + " date:" + this.date + " id:" + this.id + " body:"
		+ this.body;
    }

    public static class TextBlock {
	public final static int TEXT = 0;
	String _text;
	int _type = TEXT;

	TextBlock(String text) {
	    this._text = text;
	}

	public String getText() {
	    return this._text;
	}

	public int getType() {
	    return this._type;
	}
    }

    public abstract static class Link extends TextBlock {
	public Link(String text) {
	    super(text);
	}
    }

    public static class URILink extends Link {
	public final static int URI_LINK = 1;
	private String _uri;

	URILink(String text, String uri) {
	    super(text);
	    this._uri = uri;
	    this._type = URI_LINK;
	}

	public String getURI() {
	    return _uri;
	}
    }

    public static class InnerLink extends Link {
	public final static int INNER_LINK = 2;
	int _start;
	int _to;

	InnerLink(String text, int start, int to) {
	    super(text);
	    this._start = start;
	    this._to = to;
	    this._type = INNER_LINK;
	}

	public int getStart() {
	    return this._start;
	}

	public int getTo() {
	    return this._to;
	}
    }
}
